using System;
using System.Net.Sockets;
using OracleInternal.Common;
using OracleInternal.Secure.Network;

namespace OracleInternal.Network
{
	internal class ReaderStream
	{
		protected ConnectionOption m_conOpt;

		protected SessionContext m_sessionCtx;

		protected DataPacket m_dataPacket;

		internal bool m_EOF;

		protected OraBuf m_OraBuf;

		private byte[] m_oneByteBuffer = new byte[1];

		private byte[] m_PacketHeader = new byte[TNSPacketOffsets.NSPMKDAT + 1];

		private int m_PacketHeaderLen;

		private int m_PacketLength;

		private int m_HeaderFlags;

		private TNSPacketType m_PacketType;

		private int m_remainder;

		private int m_returnDataLength;

		private static int m_listInitialSize = 10;

		private object m_listlock = new object();

		internal ReaderStream(SessionContext sessCtx, ConnectionOption ConOpt)
		{
			m_sessionCtx = sessCtx;
			m_conOpt = ConOpt;
			m_dataPacket = new DataPacket(sessCtx);
			m_OraBuf = new OraBuf(m_dataPacket.m_dataBuffer);
		}

		internal void CheckInBandNotif_Migration()
		{
			OraBuf oraBuf = m_OraBuf;
			if (!m_sessionCtx.m_gotInBandNotif && m_PacketHeaderLen < TNSPacketOffsets.NSPDADAT)
			{
				ReadIt(oraBuf, TNSPacketOffsets.NSPDADAT - m_PacketHeaderLen);
				ProcessOB(oraBuf);
			}
		}

		private void ProcessHeaderData(OraBuf OB, ref int Length, ref int Offset, int headerLen)
		{
			int num = headerLen - m_PacketHeaderLen;
			int num2 = ((Length < num) ? Length : num);
			Buffer.BlockCopy(OB.buf, Offset, m_PacketHeader, m_PacketHeaderLen, num2);
			m_PacketHeaderLen += num2;
			Length -= num2;
			if (m_PacketHeaderLen == headerLen)
			{
				Packet.GetHeaderValues(m_PacketHeader, out m_PacketLength, out m_HeaderFlags, out m_PacketType, m_sessionCtx);
				m_remainder = m_PacketLength - headerLen;
				if (m_PacketType == TNSPacketType.DATA && m_PacketLength == 10)
				{
					int num3 = OB.buf[TNSPacketOffsets.NSPDAFLG] & 0xFF;
					num3 <<= 8;
					num3 |= OB.buf[TNSPacketOffsets.NSPDAFLG + 1] & 0xFF;
					if (num3 == DataPacket.NSPDAFEOF)
					{
						m_EOF = true;
					}
					if (m_sessionCtx.m_transportAdapter.NeedReneg && num3 == DataPacket.NSPDAFRTN)
					{
						m_sessionCtx.m_transportAdapter.Renegotiate(m_conOpt);
					}
				}
				if (ProviderConfig.m_bTraceLevelNetwork)
				{
					Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Receive, OracleTraceClassName.ReaderStream, OracleTraceFuncName.ProcessHeaderData, "New receive packet. Header: ");
					Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Receive, m_PacketHeader, 0, headerLen);
				}
			}
			Offset += num2;
		}

		private void ProcessOB(OraBuf OB)
		{
			int Offset = 0;
			if (OB.m_length == 0)
			{
				m_EOF = true;
				return;
			}
			int Length = OB.m_length;
			OB.m_length = 0;
			while (Length > 0)
			{
				if (m_PacketHeaderLen < TNSPacketOffsets.NSPDADAT)
				{
					ProcessHeaderData(OB, ref Length, ref Offset, TNSPacketOffsets.NSPDADAT);
				}
				if (m_PacketType == TNSPacketType.CTRL)
				{
					int num = TNSPacketOffsets.NSPDADAT + Length;
					int num2 = m_PacketLength - 10;
					if (ProviderConfig.m_bTraceLevelNetwork)
					{
						Trace.Write(OracleTraceLevel.Network, OracleTraceTag.None, OracleTraceClassName.ReaderStream, OracleTraceFuncName.ProcessOB, "Got a InBand Notification.");
					}
					if (num < m_PacketLength)
					{
						ReadIt(OB, m_PacketLength - num, Offset);
						Length += m_PacketLength - num;
					}
					m_sessionCtx.m_InBandNotifCmd = m_PacketHeader[TNSPacketOffsets.NSPCTLCMD] & 0xFF;
					m_sessionCtx.m_InBandNotifCmd <<= 8;
					m_sessionCtx.m_InBandNotifCmd |= m_PacketHeader[TNSPacketOffsets.NSPCTLCMD + 1] & 0xFF;
					m_sessionCtx.m_InBandNotifEMFI = OB.buf[Offset] & 0xFF;
					m_sessionCtx.m_InBandNotifEMFI <<= 8;
					m_sessionCtx.m_InBandNotifEMFI |= OB.buf[Offset + 1] & 0xFF;
					m_sessionCtx.m_InBandNotifEMFI <<= 8;
					m_sessionCtx.m_InBandNotifEMFI |= OB.buf[Offset + 2] & 0xFF;
					m_sessionCtx.m_InBandNotifEMFI <<= 8;
					m_sessionCtx.m_InBandNotifEMFI |= OB.buf[Offset + 3] & 0xFF;
					m_sessionCtx.m_InBandNotifErr1 = OB.buf[Offset + 4] & 0xFF;
					m_sessionCtx.m_InBandNotifErr1 <<= 8;
					m_sessionCtx.m_InBandNotifErr1 |= OB.buf[Offset + 4 + 1] & 0xFF;
					m_sessionCtx.m_InBandNotifErr1 <<= 8;
					m_sessionCtx.m_InBandNotifErr1 |= OB.buf[Offset + 4 + 2] & 0xFF;
					m_sessionCtx.m_InBandNotifErr1 <<= 8;
					m_sessionCtx.m_InBandNotifErr1 |= OB.buf[Offset + 4 + 3] & 0xFF;
					m_sessionCtx.m_InBandNotifErr2 = OB.buf[Offset + 8] & 0xFF;
					m_sessionCtx.m_InBandNotifErr2 <<= 8;
					m_sessionCtx.m_InBandNotifErr2 |= OB.buf[Offset + 8 + 1] & 0xFF;
					m_sessionCtx.m_InBandNotifErr2 <<= 8;
					m_sessionCtx.m_InBandNotifErr2 |= OB.buf[Offset + 8 + 2] & 0xFF;
					m_sessionCtx.m_InBandNotifErr2 <<= 8;
					m_sessionCtx.m_InBandNotifErr2 |= OB.buf[Offset + 8 + 3] & 0xFF;
					m_sessionCtx.m_gotInBandNotif = true;
					if (ProviderConfig.m_bTraceLevelNetwork)
					{
						Trace.Write(OracleTraceLevel.Network, OracleTraceTag.None, OracleTraceClassName.ReaderStream, OracleTraceFuncName.ProcessOB, "InBand Notification: Cmd=" + m_sessionCtx.m_InBandNotifCmd + ". Err1=" + m_sessionCtx.m_InBandNotifErr1 + ". Err2=" + m_sessionCtx.m_InBandNotifErr2);
					}
					if (m_sessionCtx.m_InbandNotification != null)
					{
						m_sessionCtx.m_InbandNotification(m_sessionCtx.m_InBandNotifErr1, m_sessionCtx.m_InBandNotifErr2, m_sessionCtx.m_InBandNotifEMFI, null, m_sessionCtx.m_inbandErrContext);
					}
					m_PacketHeaderLen = (m_remainder = 0);
					Length -= num2;
					Offset += num2;
				}
				if (Length > 0)
				{
					if (m_PacketType == TNSPacketType.MARKER)
					{
						m_PacketHeader[TNSPacketOffsets.NSPMKDAT] = OB.buf[Offset];
						Length--;
						Offset++;
						m_remainder--;
						MarkerPacket markerPacket = new MarkerPacket(new Packet(m_sessionCtx, m_PacketHeader));
						m_sessionCtx.m_onBreakReset = true;
						if (markerPacket.m_isResetMarker)
						{
							m_sessionCtx.m_gotReset = true;
						}
						if (ProviderConfig.m_bTraceLevelNetwork)
						{
							if (markerPacket.m_isResetMarker)
							{
								Trace.Write(OracleTraceLevel.Network, OracleTraceTag.None, OracleTraceClassName.ReaderStream, OracleTraceFuncName.ProcessOB, "Got a RESET marker packet");
							}
							else if (markerPacket.m_isBreakMarker)
							{
								Trace.Write(OracleTraceLevel.Network, OracleTraceTag.None, OracleTraceClassName.ReaderStream, OracleTraceFuncName.ProcessOB, "Got a BREAK marker packet");
							}
							Trace.Write(OracleTraceLevel.Network, OracleTraceTag.None, OracleTraceClassName.ReaderStream, OracleTraceFuncName.ProcessOB, "Marker data = " + markerPacket.m_markerData);
						}
						m_returnDataLength = 0;
					}
					if (m_remainder > 0)
					{
						int num3 = ((Length > m_remainder) ? m_remainder : Length);
						OB.AddForReceive(Offset, num3);
						if (ProviderConfig.m_bTraceLevelNetwork && !ProviderConfig.m_bTraceLevelSecure)
						{
							Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Receive, OB.buf, Offset, num3);
						}
						Length -= num3;
						m_remainder -= num3;
						Offset += num3;
					}
				}
				if (m_PacketHeaderLen == TNSPacketOffsets.NSPDADAT && m_remainder == 0)
				{
					m_PacketHeaderLen = 0;
				}
			}
		}

		private void ReadIt(OraBuf OB, int len, int offset = 0)
		{
			OB.m_length = offset;
			len += offset;
			try
			{
				do
				{
					if (m_sessionCtx.m_socket != null)
					{
						OB.m_length += m_sessionCtx.m_socket.Receive(OB.m_buf, OB.m_length, len - OB.m_length, SocketFlags.None);
					}
					else
					{
						OB.m_length += m_sessionCtx.m_socketStream.Read(OB.m_buf, OB.m_length, len - OB.m_length);
					}
				}
				while (OB.m_length != len && OB.m_length != 0);
			}
			catch (Exception inner)
			{
				throw new NetworkException(12570, inner);
			}
			if (OB.m_length == 0)
			{
				m_EOF = true;
				throw new NetworkException(12537);
			}
		}

		internal void WaitForReset()
		{
			OraBuf oraBuf = m_OraBuf;
			oraBuf.Clear();
			if (m_sessionCtx.m_gotReset)
			{
				return;
			}
			if (m_remainder > 0)
			{
				ReadIt(oraBuf, m_remainder);
				ProcessOB(oraBuf);
			}
			while (!m_sessionCtx.m_gotReset)
			{
				oraBuf.Clear();
				ReadIt(oraBuf, TNSPacketOffsets.NSPDADAT - m_PacketHeaderLen);
				ProcessOB(oraBuf);
				if (m_PacketLength - TNSPacketOffsets.NSPDADAT > 0)
				{
					ReadIt(oraBuf, m_PacketLength - TNSPacketOffsets.NSPDADAT);
					ProcessOB(oraBuf);
				}
				if (m_sessionCtx.cryptoNeeded)
				{
					HandleCrypto(oraBuf);
				}
			}
		}

		internal int Read(OraBuf OB, out int InbandErr, out string InbandErrMsg)
		{
			InbandErr = -1;
			InbandErrMsg = null;
			if (m_sessionCtx.m_onBreakReset)
			{
				throw new NetworkException(3111);
			}
			if (m_sessionCtx.cryptoNeeded)
			{
				return ReadwithCrypto(OB);
			}
			try
			{
				do
				{
					if (m_sessionCtx.m_socket != null)
					{
						OB.m_length = m_sessionCtx.m_socket.Receive(OB.m_buf, 0, OB.m_buf.Length, SocketFlags.None);
					}
					else
					{
						OB.m_length = m_sessionCtx.m_socketStream.Read(OB.m_buf, 0, OB.m_buf.Length);
					}
					ProcessOB(OB);
				}
				while (OB.m_length == 0 && !m_EOF && !m_sessionCtx.m_onBreakReset);
			}
			catch (SocketException ex)
			{
				if (ex.ErrorCode == 10053 || ex.ErrorCode == 10054)
				{
					throw new NetworkException(3135, ex);
				}
				throw new NetworkException(12570, ex);
			}
			catch (Exception inner)
			{
				throw new NetworkException(12570, inner);
			}
			if (OB.m_length == 0 && m_EOF)
			{
				throw new NetworkException(12537);
			}
			if (m_sessionCtx.m_onBreakReset)
			{
				throw new NetworkException(3111);
			}
			return OB.m_length;
		}

		private int ReadwithCrypto(OraBuf OB)
		{
			try
			{
				do
				{
					OB.Clear();
					ReadIt(OB, TNSPacketOffsets.NSPDADAT - m_PacketHeaderLen);
					ProcessOB(OB);
					if (m_PacketLength - TNSPacketOffsets.NSPDADAT > 0)
					{
						ReadIt(OB, m_PacketLength - TNSPacketOffsets.NSPDADAT);
						ProcessOB(OB);
					}
				}
				while (OB.m_length == 0 && !m_EOF && !m_sessionCtx.m_onBreakReset);
			}
			catch (SocketException ex)
			{
				if (ex.ErrorCode == 10053 || ex.ErrorCode == 10054)
				{
					throw new NetworkException(3135, ex);
				}
				throw new NetworkException(12570, ex);
			}
			catch (Exception inner)
			{
				throw new NetworkException(12570, inner);
			}
			if (OB.m_length == 0 && m_EOF)
			{
				throw new NetworkException(12537);
			}
			HandleCrypto(OB);
			if (m_sessionCtx.m_onBreakReset)
			{
				throw new NetworkException(3111);
			}
			return OB.m_length;
		}

		private void HandleCrypto(OraBuf OB)
		{
			if (OB.m_length <= 0)
			{
				return;
			}
			int num = 0;
			OraArraySegment[] the_ByteSegments = OB.the_ByteSegments;
			if (OB.the_ByteSegments_Count <= 0)
			{
				throw new NetworkException(12570, new NetworkException(12566));
			}
			OraArraySegment oraArraySegment = the_ByteSegments[0];
			if (OB.the_ByteSegments_Count == 1)
			{
				num = the_ByteSegments[0].Count;
			}
			else
			{
				for (int i = 1; i < OB.the_ByteSegments_Count; i++)
				{
					OraArraySegment oraArraySegment2 = the_ByteSegments[i - 1];
					OraArraySegment oraArraySegment3 = the_ByteSegments[i];
					if (oraArraySegment2.Array != oraArraySegment3.Array || oraArraySegment2.Array[oraArraySegment2.Offset + oraArraySegment2.Count] != oraArraySegment3.Array[oraArraySegment3.Offset])
					{
						throw new NetworkException(12570, new NetworkException(12566));
					}
					num += oraArraySegment2.Count;
				}
				num += the_ByteSegments[OB.the_ByteSegments_Count - 1].Count;
			}
			EncryptionAlgorithm encryptionAlg = m_sessionCtx.encryptionAlg;
			DataIntegrityAlgorithm dataIntegrityAlg = m_sessionCtx.m_ano.dataIntegrityAlg;
			int num2 = oraArraySegment.Count - 1;
			byte[] array;
			if (oraArraySegment.Offset == 0)
			{
				array = oraArraySegment.Array;
			}
			else
			{
				array = new byte[num2];
				Buffer.BlockCopy(oraArraySegment.Array, oraArraySegment.Offset, array, 0, num2);
			}
			byte[] array2;
			if (encryptionAlg != null)
			{
				array2 = encryptionAlg.decrypt(array, num2);
				num2 = array2.Length;
			}
			else
			{
				array2 = array;
			}
			if (dataIntegrityAlg != null)
			{
				if (dataIntegrityAlg.compare(array2, num2 - dataIntegrityAlg.size(), array2, num2 - dataIntegrityAlg.size()))
				{
					throw new NetworkException(12599);
				}
				num2 -= dataIntegrityAlg.size();
			}
			OB.Clear();
			OB.AddForReceive(array2, 0, num2);
		}

		internal int Read()
		{
			if (Read(m_oneByteBuffer, 0, 1) >= 0)
			{
				return m_oneByteBuffer[0] & 0xFF;
			}
			return -1;
		}

		internal int ReadOne()
		{
			return Read();
		}

		internal int Read(byte[] userBuffer)
		{
			return Read(userBuffer, 0, userBuffer.Length);
		}

		internal int Read(byte[] userBuffer, int offset, int length)
		{
			int num = 0;
			if (length <= 0)
			{
				throw new NetworkException(12532);
			}
			if (m_sessionCtx.m_usingAsyncReceives)
			{
				throw new NetworkException(12623);
			}
			if (m_sessionCtx.m_onBreakReset)
			{
				throw new NetworkException(3111);
			}
			try
			{
				do
				{
					if (m_dataPacket.m_availableBytesToRead <= 0 || m_dataPacket.m_type == TNSPacketType.NULL)
					{
						getNextPacket();
					}
					num += m_dataPacket.getDataFromBuffer(userBuffer, offset + num, length - num);
				}
				while (num < length);
				return num;
			}
			catch (SocketException)
			{
				return num;
			}
		}

		internal void getNextPacket()
		{
			if (m_sessionCtx.m_onBreakReset)
			{
				throw new NetworkException(3111);
			}
			if (m_sessionCtx.m_writerStream.m_dataPacket.m_availableBytesToSend > 0)
			{
				m_sessionCtx.m_writerStream.Flush();
			}
			m_dataPacket.Receive();
			switch (m_dataPacket.m_type)
			{
			case TNSPacketType.MARKER:
			{
				MarkerPacket markerPacket = new MarkerPacket(m_dataPacket);
				m_sessionCtx.m_onBreakReset = true;
				if (markerPacket.m_isResetMarker)
				{
					m_sessionCtx.m_gotReset = true;
				}
				throw new NetworkException(3111);
			}
			default:
				throw new NetworkException(12592);
			case TNSPacketType.DATA:
			case TNSPacketType.NULL:
				break;
			}
		}
	}
}
